package cn.kgc.filter;


import cn.hutool.json.JSONUtil;
import cn.kgc.grace.result.ResponseStatusEnum;
import cn.kgc.utils.ExcludeProperties;
import cn.kgc.utils.JwtUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import static cn.kgc.base.SystemConstant.*;

/**
 * @author YC
 */
@Component
public class SecurityJwtFilter implements GlobalFilter, Ordered {

    @Resource
    private ExcludeProperties excludeProperties;
    @Resource
    private JwtUtil jwtUtil;
    /**
     * 路径匹配规则器
     */
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 将不需要验证令牌的请求地址直接放行
        String path = exchange.getRequest().getPath().toString();
        boolean isExclude = excludeProperties.getUrls().stream()
                .anyMatch(url -> antPathMatcher.match(url, path));
        if (isExclude) {
            return chain.filter(exchange);
        }
        // 从请求头获取令牌，令牌验证正确则获取用户信息，不正确则返回错误信息
        String token = exchange.getRequest().getHeaders().getFirst(LOGIN_TOKEN);
        try {
            String userId = jwtUtil.getUserId(token);
            String loginType = jwtUtil.getLoginType(token);
            setNewHeader(exchange, HEAD_USER_ID, userId);
            setNewHeader(exchange, HEAD_LOGIN_TYPE, loginType);
        } catch (Exception e) {
            // TODO 系统错误日志信息的存储
            return renderErrorMsg(exchange);
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 设置新的请求头
     *
     * @param exchange    请求
     * @param headerKey   请求头key
     * @param headerValue 请求头value
     * @return 新的请求
     */
    private ServerWebExchange setNewHeader(ServerWebExchange exchange, String headerKey, String headerValue) {
        ServerHttpRequest newRequest = exchange.getRequest()
                .mutate()
                .header(headerKey, headerValue)
                .build();
        return exchange.mutate().request(newRequest).build();
    }

    /**
     * 返回令牌错误信息
     *
     * @param exchange 请求
     * @return 错误信息
     */
    private Mono<Void> renderErrorMsg(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        Map<String, Object> result = new HashMap<>(4);
        result.put("status", ResponseStatusEnum.UN_LOGIN.status());
        result.put("msg", ResponseStatusEnum.UN_LOGIN.msg());
        result.put("data", null);
        result.put("success", false);
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        DataBuffer wrap = response.bufferFactory()
                .wrap(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(wrap));
    }

}
